/*
 * nmats.c
 *
 *  Created on: Feb 25, 2009
 *      Author: Marco Luisi
 */

//riguardarsi gli include
#include "nmats.h"
#include "matrix.h" // basta nel .h?
#include "sysmacro.h" // da tnere?
#include <ctype.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>


#define forever for(;;) // da spostare in un util.h da fare
#define N 10 //massimo numero utenti, vedere di non usarlo
#define MSG 32 // PID CONNECT user  user= 30 nel codice.. rivedere
#define WDIR nmat_data // rinominare

#define ONLINE 1
#define OFFLINE 0

//stioccare nel .h ?
void makedir(char*); // int per gli errori?
void init(); //tutte void?
void listen();
void spawn(char*, char*);//perchè non un array di 2 char*?
int validate(char*, user[]); // cambiare nome?ù //oppure fare in modo non serva il riferimento

//static void handler(int);

FILE *fd;

user *users; //vedere di togliere il limite

int allocated;

int usersnum; //rinominare
char **aux;
char *buf;
char *string;
//char *msg; //cambiare in login_msg?
size_t len = 0;
//ssize_t read;
int u;
int i;
int pid;
char nmat_data[] = "WDIR"; // decidere che farne
DIR * wdir = NULL; // da nullificare?
struct sigaction sg;

int server_pipe;
int server_pipe_aux; // dal libro

pid_t pid;






//fare array dimensionato con num utenti
//con gli fd delle rispettive pipe->utenti

// USARE STRDUP DOVE SI PUO'
int main(int argc, char **argv) {
	// da fare una cleanup() che toglie pattume in caso di precedente crash
	pid = getpid(); // non serve a una mazza?

		if (argc != 2) {
			WRITELN("NMAT Server: errore, specificare una lista di utenti!");
			exit(0); // guardare o 1?
		}

		fd = fopen(argv[1], "r"); //da input o fisso?

		if (fd == NULL) {
			WRITELN("NMAT Server: errore in lettura della lista utenti!");
			exit(EXIT_FAILURE); // brutto?
		}

		init();
		// aa
		listen();

		free(buf);
		free(string);
		//free(fd);
		free(users); // non vabene o si...?
		//opendir("..");
		printf("unlink:%d\n", unlink("../clientserver")); /* cancello clientserver se c'e gia' *///serve?
		perror("unlink:");

		WRITELN("NMAT Server in chiusura..");
		WRITELN("Arrivederci.");

		exit(0);




	return 0;
} // fine main

//static void handler(int signum) {
//	printf("Ricevuto segnale %d\n", signum);
//	exit(EXIT_FAILURE);
//}

void init() { // lasciare void?
// FORSE FORSE ARRAY CON GLI FD delle pipe cs/sc da passare agli worker?
//	bzero(&sg, sizeof(sg)); //deprecato
//	sg.sa_handler = handler;
	//err_meno1( sigaction(SIGINT, &sg, NULL) );


	/* non fallisce se la pipe c’è gia’ */
	/* creo pipe con nome clientserver */

	printf("unlink:%d\n", unlink("./clientserver")); /* cancello clientserver se c'e gia' *///serve?
	perror("unlink:");
	IFERROR(mkfifo("clientserver",0666),"creazione del pipe con nome"); //rivedere la maschera
	/* apro la pipe clientserver in lettura */
	// ok rdwr?
	//su blocca fino al primo client, e spostarlo piu avanti?
	IFERROR(server_pipe = open("clientserver",O_RDONLY ), "server: apro clientserver"); //O_NONBLOCK
	IFERROR(server_pipe_aux = open("clientserver",O_WRONLY), "server: AUX");//per evitare gli eof!!

	string = malloc(30 * sizeof(char)); // togliendo questa string da null.., provare ad aggiustare la dimensione
	usersnum = 0;
	// -1 a EOF a regola
	//se piu nomi su riga divisi da whitespace,prende solo il primo
	//ma le specifiche sono chiare..
	buf = malloc(30 * sizeof(char)); // funziona ma come sistemare queste dimensioni??
	allocated = 4; //poi mettere macro
	users = malloc(allocated * sizeof(user));

	while (fgets(buf, 30, fd) != NULL) { //ok questo modo di riallocare?
		if (usersnum == allocated) {			//ok riallocare per se?
			printf("realloc\n");
			//SEGNALI!!!
			allocated *= 2; //raddoppia la dimensione array
			users = (user*)realloc(users, allocated * sizeof(user)); // forse size of user*?
			if (users == NULL) { //da cambiare con la lista struct
				//fatal ("Virtual memory exhausted");
				printf("realloc fail\n");
				return; // SEGNALE?! //atensioòòn che init è void
			}

		}

		if (sscanf(buf, "%s", string) != -1) { //di suo scarta tutto scanf tranne \n
			//add user, farne funzione a se?
			user temp;
			temp.name = calloc(strlen(string) + 1, sizeof(char)); //rivedere +1
			temp.name = strdup(string);
			temp.status = 0;
			printf("name %s status %d \n",temp.name, temp.status);
			users[usersnum] = temp;
			//fine add user
			printf("Utente %s abilitato.\n", users[usersnum].name);
			usersnum++; //forse era emeglio una var a parte per scorrere come era prima
			}
	}
	fclose(fd);
	printf("Abilitati %d utenti al server.\n", u); //etico? e questo u?


	//users = realloc(users, usersnum * sizeof(user)); //

	for (i = 0; i < u; i++) { //riguardarsi sti controlli
		printf("%d:%s\n", i, users[i].name); // !! DA FARE CONTROLLO BOUNDS ARRAY !!!
	}

	// CREAZIONE DIRECTORY MAGARI FUNZIONE A PARTE
	//DECIDERE DOVE USARE SC E NO PER I FILE
	// da aprire la dir corrente?
	//struct dirent *ep; //rinominare e spostare
	wdir = NULL; //serve?
	wdir = opendir("nmat_data"); // imparare i permessi...
	perror("creazione directory:");
	if (wdir != NULL) {
		WRITELN("Directory di lavoro aperta.");
	} else {
		makedir("nmats_data"); // la macroooo
		WRITELN("Directory di lavoro creata.");
	}

	// creazione directory nmats
	if (chdir("nmats_data") == -1) { //capire perchè wdir definito senza " no nva
		perror("chdir");
		exit(errno);
	}
	//CREAzione directory degli utenti
	int x; // magari riusare i azzerandola
	for (x = 0; x < usersnum; x++) { // usare sempre u come riferimento per l'array è giusto?
		makedir(users[x].name); //cambiare la x
		//printf("Directory creata per l'Utente %s .\n", users[x]);
	}
	// CHIUDERE DIR E PIPES sopratutto al posto giusto
	// RICONTROLLARE LE FREE!!!!!!!!!!

}
//idea per la dim del pid.. calcolarlo in base alle
//cifre del pid del server..




/* Listen
 * nel caso ciclo infinito prova
 * ciclo lettura pipe
 * controllo msg-> nome
 * fork exec
 */

//vedere quali variabili servono fuori dallo scope di listen
void listen() { //cambiarne nome
	//IN LINEA D MASSIMA SI PUO' usare
	// un singolo buffer per ricevere/inviare ?
	char *msg;
	msg = malloc(30 * sizeof(char));
	char *buffer;
	buffer = malloc(30 * sizeof(char));
	//msg = calloc(30, sizeof(char)); //rivedersi m/alloc
	int numread = 0; //spostare?
	int outc = 0; //out character
	int auth = 0; // 1 ok , 0 no, -1 giàlloggato
	int dim = 0; //provare senzaassegnare
	char *cpid; // da spostare?
	char *username; // perchè prende user.list?? prima d
	username = malloc(30 * sizeof(char));
	cpid = malloc(6 * sizeof(char));
	char *response; // capire bene come memorizzare queste var!!
	response = malloc(30 * sizeof(char));
	int k = 0; // prima che strtok ci scriva su?
	char *o_pipe;
	o_pipe = malloc(30 * sizeof(char)); //spostare
	int out;
	WRITELN("inizio ciclo listen");
	while (k < 5) { //FOREVAH
		WRITELN("2 sec sleep prima della read");
		sleep(2);
		numread = read(server_pipe, buffer, 30);
		printf("giunse !%s!\n", buffer);
		outc = snprintf(msg, (size_t)numread+1 , "%s", buffer); //da capire perchè serve +1

		if (numread > 0) {
			cpid = strtok(msg, " ");
			username = strtok(NULL, " "); //riuso username tanto per
			// controllo sul comando
			// pensare come gestire i comandi..
			// ma qui prima del fork prende solo connect..
			if (strcmp(username, "CONNECT") != 0) {//farne un literal?
				WRITELN("errore in connessione"); // o cmq rendere piu elegante il parsing
				return; // sistemare
			}
			username = strtok(NULL, " ");
			username =  strcat(username, "\0");
			//sprintf(username, "%s", strtok(NULL, " "));
			printf("validate di %s\n",username);
			sprintf(o_pipe,"%s%s","../serverclient",cpid);
			out = open(o_pipe, O_WRONLY);
			auth = validate(username, users);
			dim = sprintf(response,"%s%d","LOGIN ",auth); //il messaggio viene spedito a prescindere
			printf("invio dopo validate: %s\n", response);
			write(out, response, dim);//cambiare numread
			if (auth == 0) { // mettere in modo univoco significato 0/1
				spawn(cpid,username);
			}
			 // se 1 o -1 invia ma non succede altro
			else {
				WRITELN("utente non autenticato");
			}


		} else {
			WRITELN("letto niente"); // sto ramo se fatta bne la read non dovrebber servire..
		}
		//fflush(server_pipe);
		//username = NULL;
		k++;
	}
	WRITELN("fine ciclo listen");
	//parse con strtok


}

//rifarla in modo serio

//per ora da -1
int validate (char *username, user u[]) { //o forse meglio il riferimento?
	int ret = 1; // na bella define per 0/1?
	int i = 0;
	// 1 non c'è -1 gialloggato 0 ok
	//printf("validate di %s\n",username);
	while ( ret && i < usersnum){ //vedersi bene regole di scope
		ret = strcmp(username, u[i].name); //strcmp da 0 per match, quindi..
		i++;
	}
	if (ret == 0) { //
		if (u[i].status){
			ret = -1;
			//qui la modifica
		}
		else {
			u[i].status = 1;
		}
	}
	return ret; // razionalizzare i return
}


void spawn(char* cpid, char* username) { //sembra non prendere l'username tra due chiamate vicine
	printf("Creazione del processo worker per l'utente %s:%s \n", username, cpid);
	pid = fork();
	//perror("fork:");
	if (pid == 0) {  // etico fare qui il controllo?
		WRITELN("FIJO!!");
		execl("/home/marco/workspace/worker/Debug/worker", "worker", username, cpid,(char*)NULL); //basta NULL?
			perror("exec");
			//WRITELN("sleep 2 secondi dopo exec");

			sleep(2);
			return;
 }

	WRITELN(" WORKER CREATO!");

}





void makedir(char *dir_name) { //riferimento?
	//STABILIRE SE E' SYSCALL O NO!!!:D
	//DARE RIPULITA a questa funz
	if ((mkdir(dir_name, O_CREAT | 0666)) == -1) { // O_CREAT nonserve forse
		if (errno == EEXIST) {
			printf("Directory %s già presente.\n", dir_name);
			return; //magari bruttino?

		} else {
			perror("errore"); // capire questa errno sulla dir esistente..
			exit(errno);
		}

	}
	printf("Directory %s creata.\n", dir_name);
}


